﻿using System;
using System.Xml;
using System.Xml.Linq;
using AutoMapper;

namespace SPContrib
{
    /// <summary>
    /// Utility class for registering and resolving type mappings beteen types
    /// </summary>
    public static class TypeMapper
    {

        static TypeMapper()
        { 
            // string => string
            Mapper.CreateMap<string, string>()
                    .ConvertUsing(s => s);

            // string => int
            Mapper.CreateMap<string, int>()
                    .ConvertUsing(s => Convert.ToInt32(s));

            // string => DateTime
            Mapper.CreateMap<string, DateTime>()
                    .ConvertUsing(s => Convert.ToDateTime(s));

            // string => bool
            Mapper.CreateMap<string, bool>()
                    .ConvertUsing(s => Convert.ToBoolean(s));

            // XElement => XmlDocument
            Mapper.CreateMap<XElement, XmlDocument>()
                    .ConvertUsing<TypeConverters.XElementToXmlDocument>();

            // XmlNode => XElement
            Mapper.CreateMap<XmlNode, XElement>()
                    .ConvertUsing<TypeConverters.XmlNodeToXElement>();

            // TODO: object => T (i.e. any type)
                    
                    
        }

        /// <summary>
        /// Indicates if a mapping exists between two types
        /// </summary>
        /// <typeparam name="TSource">the source type</typeparam>
        /// <typeparam name="TDestination">the destination type</typeparam>
        public static bool CanResolveBetween<TSource, TDestination>()
        {
            return CannotResolveBetween<TSource, TDestination>() == false;
        }

        /// <summary>
        /// Indicates if a mapping doesn´t exists between two types. (i.e. the opposite of CanResolveBetween)
        /// </summary>
        /// <typeparam name="TSource">the source type</typeparam>
        /// <typeparam name="TDestination">the destination type</typeparam>
        public static bool CannotResolveBetween<TSource, TDestination>()
        {
            var map = Mapper.FindTypeMapFor<TSource, TDestination>();

            return object.Equals(map, null);
        }

        /// <summary>
        /// Resolves (i.e. creates the mapping) beteen a source type and a destination type
        /// </summary>
        /// <typeparam name="TSource">the source type</typeparam>
        /// <typeparam name="TDestination">the destination type</typeparam>
        /// <param name="source">an instance of the source type to resolve as the destination type</param>
        /// <exception cref="System.Exception" />
        public static TDestination Resolve<TSource, TDestination>(TSource source)
        {
            if (CanResolveBetween<TSource, TDestination>() == false)
            {
                throw new Exception(
                    string.Format("TypeMapping: No mapping is registred between '{0}' and '{1}'",
                                    typeof(TSource).Name, typeof(TDestination).Name));
            }
            return Mapper.Map<TSource, TDestination>(source);
        }

        /// <summary>
        /// Registers a new mapping between a source type and a destination type. It also checks if the mapping already exists.
        /// </summary>
        /// <typeparam name="TSource">the source type</typeparam>
        /// <typeparam name="TDestination">the destination type</typeparam>
        /// <param name="converter">represents a method that converts from the source type to the destination type</param>
        public static void RegisterMappingBetween<TSource, TDestination>(Func<TSource, TDestination> converter)
        {
            if (CannotResolveBetween<TSource, TDestination>())
            {
                Mapper.CreateMap<TSource, TDestination>()
                        .ConvertUsing(converter);
            }
        }
    }
}
